package cc.linker.steplink.crm.task;

import cc.linker.steplink.crm.commons.lock.RedisLock;
import cc.linker.steplink.crm.service.IClientRobotService;
import cc.linker.steplink.crm.service.ISysConfig;
import com.mongodb.util.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 智能客户专员机器人阶段二
 * @Author: wuyue
 * @Date: 2019/9/2 11:01
 * @Version: v1.8.3
 */
@Slf4j
@Component
public class ClientRobotPhaseII {

    private static final DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S");
    // 推送时间配置名
    private static final String CLIENT_ROBOT_START_TIME = "clientRobotStartTime";
    // 用户锁key
    private static final String WXPUSHLIST_LOCK = "wxpushlist_lock";
    // 推送消息集合key
    private static final String WXPUSHLIST = "wxpushlist";
    // 默认推送时间
    private static final String DEFAULT_PUSH_TIME = "08:00:00";

    @Autowired
    private IClientRobotService clientRobotService;
    @Autowired
    private ISysConfig sysConfigService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @RedisLock("ClientRobotPhaseII")
    @Scheduled(cron = "0 3/20 * * * ?")
    public void go() {
        LocalDateTime lastOfMonth = LocalDateTime
                .now()
                .with(TemporalAdjusters.lastDayOfMonth())
                .withHour(23)
                .withMinute(59)
                .withSecond(59);
        // 当月最后一天
        Date date = Date.from(lastOfMonth.atZone(ZoneId.systemDefault()).toInstant());

        // 客户机器人开启的机构下所有存在全套营销标签的用户的customerId,orgId,labelId,customerLabelLevel
        // 后期客户量大的情况下这一步可以只取customerId其他数据库读取操作在循环中进行，减少单次IO和内存开销
        List<Map<String, Object>> userArticleInfo = clientRobotService.getUserArticleInfo();
        Random random = new Random();
        for (Map<String, Object> map : userArticleInfo) {
            String openid = (String) map.get("openid");
            String token = (String) map.get("token");
            if (StringUtils.isEmpty(openid) || StringUtils.isEmpty(token)) {
//                LOGGER.warn("用户信息不完整,Incomplete User Information" + map);
                continue;
            }
            // redis中查找用户是否已有待发送文章
            Boolean isMember = redisTemplate.opsForSet().isMember(WXPUSHLIST_LOCK, token + openid);
            // 如果是，则跳过
            if (isMember != null && isMember) {
                continue;
            }

            // 生成用户推送文章范围,推送文章范围=营销文章库-用户已经推送的文章,labelId,articleId,title,image,desc,url
            // 后期数据量大的情况下这一步可以只取articleId和labelId
            List<Map<String, Object>> userPushArticle = clientRobotService.getUserPushArticle(openid, token);
            if (userPushArticle == null || userPushArticle.size() == 0) {
                LOGGER.warn("用户没有可推送文章,Users do not have push articles");
                continue;
            }

            // 从推送文章范围中取出文章标签
            List<String> articleLabels = new ArrayList<>();

            for (Map<String, Object> pushArticle : userPushArticle) {
                String labelId1 = (String) pushArticle.get("labelId");
                if (!StringUtils.isEmpty(labelId1)) {
                    String[] split1 = labelId1.split(",");
                    articleLabels.addAll(Arrays.asList(split1));
                }
            }

            if (articleLabels.size() == 0) {
                continue;
            }

            // 去重
            articleLabels = articleLabels
                    .stream()
                    .distinct()
                    .collect(Collectors.toList());

            // a b c d e f g, 1 2 3 4 5 6 7
            // 标签等级集合
            String customerLabelLevel = (String) map.get("customerLabelLevel");
            String[] customerLabelLevels = customerLabelLevel.split(",");
            // 标签id集合(与等级集合顺序对应),即：a对应1,b对应2,customerLabelLevels[i]对应labelIds[i]等等
            String labelId = (String) map.get("labelId");
            String[] labelIds = labelId.split(",");

            // 去除不在文章标签集合中的(权重置为0)
            for (int i = 0; i < labelIds.length; i++) {
                if (!articleLabels.contains(labelIds[i])) {
                    customerLabelLevels[i] = "0";
                }
            }

            // 总权重,即1+2+3...+6+7
            int sum = 0;
            // 权重数组,形如[1,3,6,10,15,21,28]
            int[] weight = new int[customerLabelLevels.length];
            int i = 0;
            for (String s : customerLabelLevels) {
                sum += Integer.parseInt(s);
                weight[i] = sum;
                i++;
            }

            if (sum == 0) {
//                LOGGER.warn("无符合文章");
                continue;
            }

            LOGGER.info("weight = " + Arrays.toString(weight));
            // 生成随机数
            int randomNum = random.nextInt(sum);
            // 被选中的标签id
            String selectedLabelId = "";
            LOGGER.info("randomNum = " + randomNum);

            // 根据随机数选择标签
            for (int j = 0; j < weight.length; j++) {
                // 落在区间内
                if (randomNum < weight[j]) {
                    LOGGER.info("j = " + j);
                    selectedLabelId = labelIds[j];
                    break;
                }
            }
            LOGGER.info("selectedLabelId = " + selectedLabelId);

            // 根据标签id寻找文章(要没发过的)
            // 根据选出的标签id，从userPushArticle中选取一篇文章,先移除没有该标签的文章
            Iterator<Map<String, Object>> iterator = userPushArticle.iterator();
            while (iterator.hasNext()) {
                String labelId1 = (String) iterator.next().get("labelId");
                if (!labelId1.contains(selectedLabelId)) {
                    iterator.remove();
                }
            }

            // 随机数，去list中随机选取一篇文章
            int randomNum2 = random.nextInt(userPushArticle.size());
            // 选取到的文章,articleId,image,title,url,desc
            Map<String, Object> article = userPushArticle.get(randomNum2);

            // 根据 推送频次和推送记录，判断该用户是否到了该推送的日期，到了则进行下一步，否则跳过
            Integer frequency = (Integer) map.get("frequency");
            // 机构id
            String orgId = (String) map.get("orgId");
            // 推送时间：时分秒10：11：12
            String pushTime = sysConfigService.getSysConfByName(orgId, CLIENT_ROBOT_START_TIME);
            // 如果机构没设置过，默认为08:00:00
            if (StringUtils.isEmpty(pushTime)) {
                pushTime = DEFAULT_PUSH_TIME;
            }
            // 分成 [11,12,13]
            String[] split = pushTime.split(":");
            if (split.length < 3) {
                LOGGER.warn("当前机构推送时间格式设置错误");
                continue;
            }
            // 最后一次发送的时间
            String lastSendTime = clientRobotService.getLastPushTimeByOpenIdAndToken(openid, token);
            // 推送时间
            LocalDateTime localDateTime;
            if (StringUtils.isEmpty(lastSendTime)) {
                // 没发送过,获取当前时间
                localDateTime = LocalDateTime
                        .now();
            } else {
                // 发送过,获取上次发送的时间加上频次
                localDateTime = LocalDateTime
                        .parse(lastSendTime, df)
                        .plusDays(frequency);
            }
            // 下次发送时间, 加上时分秒
            localDateTime = localDateTime
                    .withHour(Integer.valueOf(split[0]))
                    .withMinute(Integer.valueOf(split[1]))
                    .withSecond(Integer.valueOf(split[2]));
            LocalDateTime startTime = LocalDate.now().atTime(0, 0, 0);
            LocalDateTime endTime = LocalDate.now().atTime(23, 59, 59);
            // 如果不是今天推送，跳过
            if (!localDateTime.isAfter(startTime) || !localDateTime.isBefore(endTime)) {
                continue;
            }
            // 推送时间戳
            long sendTime = localDateTime
                    .toInstant(ZoneOffset.of("+8"))
                    .toEpochMilli() / 1000;
            // 组装参数
            article.put("time", sendTime);
            article.put("type", "client");
            article.put("openid", openid);
            article.put("token", token);
            article.put("orgId", orgId);
            // 将文章加入待推送集合
            redisTemplate.opsForSet().add(WXPUSHLIST, JSON.serialize(article));
            redisTemplate.expireAt(WXPUSHLIST, date);
            // 将用户加入待推送集合
            redisTemplate.opsForSet().add(WXPUSHLIST_LOCK, token + openid);
            redisTemplate.expireAt(WXPUSHLIST_LOCK, date);

        }
    }


}
